package ip

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"regexp"
	"strconv"
)

var activeSiteIndex = 0

// GetPublicIPAddress 使用预定义的服务器列表查询当前 IP 地址
func GetPublicIPAddress() (string, error) {
	// 尝试查询 IP 地址的次数
	var triedCount = 0
	// 可尝试的服务器的个数
	var sitesLen = len(Sites)
	// 当前站点信息
	var currentSite *SiteInfo

	// 检查预定义常量
	if sitesLen == 0 {
		return "", errors.New("no available sites can be used")
	}

	for {
		// 尝试次数累加
		triedCount++
		// 取出服务器信息
		currentSite = &Sites[activeSiteIndex]

		// 查询 IP 地址
		log.Printf("trying to retrieve ip address from %v\n", currentSite.url)
		response, err := http.Get(currentSite.url)
		if err != nil {
			// 记录错误日志
			log.Printf("tried %s failed: %v", currentSite.url, err)

			// 若对于站点列表的每一个地址都尝试过，则返回失败
			if triedCount == sitesLen {
				return "", err
			}

			// 若还有未尝试的服务器，则继续尝试
			activeSiteIndex = (activeSiteIndex + 1) % sitesLen
			continue
		}

		// 若服务器返回了错误，则继续尝试其他地址
		if response.StatusCode != 200 {
			// 记录错误日志
			log.Printf("server %v returned %v\n", currentSite.url, response.StatusCode)

			// 若所有地址都尝试过，则返回一个网络错误
			if triedCount == sitesLen {
				return "", errors.New("server returned " + strconv.Itoa(response.StatusCode))
			}

			// 若还有未尝试的服务器，则继续尝试
			activeSiteIndex = (activeSiteIndex + 1) % sitesLen
			continue
		}

		bs, err := ioutil.ReadAll(response.Body)
		if err != nil {
			log.Printf("retrieve response body failed: %v", err)

			// 若对于站点列表的每一个地址都尝试过，则返回失败
			if triedCount == sitesLen {
				return "", err
			}

			// 若还有未尝试的服务器，则继续尝试
			activeSiteIndex = (activeSiteIndex + 1) % sitesLen
			continue
		}

		// 解析 IP 地址
		ipAddress := ""
		ipAddressRe, parseError := regexp.Compile("\\d+\\.\\d+\\.\\d+\\.\\d+")
		if parseError == nil {
			responseBody := string(bs)
			switch currentSite.siteType {
			case SiteTypePlaintext:
				ipAddress = ipAddressRe.FindString(responseBody)
			case SiteTypeRegEx:
				ipAddress = ipAddressRe.FindString(responseBody)
			default:
				return "", errors.New("unrecognized site type found")
			}
			// 检查 IP 地址是否正确
			if ipAddress == "" {
				parseError = fmt.Errorf("unable to parse IP address from %v", responseBody)
			}
		}

		// 处理解析错误
		if parseError != nil {
			log.Printf("parse response failed: %v", parseError)

			// 若对于站点列表的每一个地址都尝试过，则返回失败
			if triedCount == sitesLen {
				return "", parseError
			}

			// 若还有未尝试的服务器，则继续尝试
			activeSiteIndex = (activeSiteIndex + 1) % sitesLen
			continue
		}

		log.Printf("retrieved ip address %v from %v\n", ipAddress, currentSite.url)
		return ipAddress, nil
	}
}
